﻿using System;
using System.Collections.Generic;
using System.Linq;
using Loki.Bot;
using Loki.Game;
using Loki.Game.GameData;
using Loki.Game.Inventory;
using Loki.Game.Objects;
using Loki.TreeSharp;
using Loki.Utilities;
using log4net;
using Action = Loki.TreeSharp.Action;

namespace toNyxFaceBreaker
{

    public class Facebreaker : CombatRoutine
    {

        #region CombatRoutine Implementations

            private static Player Me { get { return LokiPoe.ObjectManager.Me; } }
            private PrioritySelector BuffComposite { get; set; }
            private PrioritySelector CombatComposite { get; set; }

            private bool _eventsHooked;

            /// <summary> Gets the name. </summary>
            /// <value> The name. </value>
            public override string Name { get { return "toNyxFacebreaker"; } }

            /// <summary> Gets the buff behavior. </summary>
            /// <value> The buff composite. </value>
            public override Composite Buff { get { return BuffComposite; } }

            /// <summary> Gets the combat behavior. </summary>
            /// <value> The combat composite. </value>
            public override Composite Combat { get { return CombatComposite; } }

            /// <summary>
            ///     Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
            /// </summary>
            /// <filterpriority>2</filterpriority>
            public override void Dispose() { }

            /// <summary>
            ///     Performs weaponswap
            /// </summary>
            public void SwapWeapons() { LokiPoe.Input.PressKey(LokiPoe.ConfigManager.GetActionKey(ActionKey.WeaponSwap).Item1); }

            /// <summary> Initializes this <see cref="CombatRoutine" />. </summary>
            public override void Initialize()
            {
                if (_eventsHooked) return;
                // When we start the bot, we need to re-register all the available spells. This allows us to swap out skills
                // Without restarting the bot.

                BotMain.OnStart += bot => DoSpellRegistrations();
                _eventsHooked = true;
            }

            private void DoSpellRegistrations()
            {
                CombatComposite = new PrioritySelector(context => Target.BestTarget);
                BuffComposite = new PrioritySelector(context => Target.BestTarget);

                // If the BestTarget is null, we should not do anything.
                CombatComposite.AddChild(
                    new Decorator(ret => Target.BestTarget == null,
                        new Action(ret => RunStatus.Success))
                    );

                // Ensure we add the flask logic *first*
                // We don't want to be trying to do buffs and whatnot, if we have to pop a flask.
                BuffComposite.AddChild(CreateFlaskLogic());

                CombatComposite.AddChild(Composites.CreateMoveIntoRange(45));

                // Support for the proximity shield mobs.
                // Quite annoying when they have this!
                CombatComposite.AddChild(
                    new Decorator(ret => Target.BestTarget.HasAura("proximity_shield_aura"),
                        Composites.CreateMoveIntoRange(10)));

                Log.Debug("Registering main abilities.");
                RegisterMainAbilities();
            }

            #endregion

        private new static readonly ILog Log = Logger.GetLoggerInstanceForType();
        public static bool IsMeleeBased = false;

        #region Flask Logic

        private readonly WaitTimer _flaskCd = new WaitTimer(TimeSpan.FromSeconds(0.5));

        private static IEnumerable<InventoryItem> LifeFlasks
        {
            get
            {
                IEnumerable<InventoryItem> inv = LokiPoe.ObjectManager.Me.Inventory.Flasks.Items;
                return from item in inv
                    let flask = item.Flask
                    where flask != null && flask.HealthRecover > 0 && flask.CanUse
                    orderby flask.IsInstantRecovery ? flask.HealthRecover : flask.HealthRecoveredPerSecond descending
                    select item;
            }
        }

        private static IEnumerable<InventoryItem> ManaFlasks
        {
            get
            {
                IEnumerable<InventoryItem> inv = LokiPoe.ObjectManager.Me.Inventory.Flasks.Items;
                return from item in inv
                    let flask = item.Flask
                    where flask != null && flask.ManaRecover > 0 && flask.CanUse
                    orderby flask.IsInstantRecovery ? flask.ManaRecover : flask.ManaRecoveredPerSecond descending
                    select item;
            }
        }

        private IEnumerable<InventoryItem> GraniteFlasks
        {
            get
            {
                IEnumerable<InventoryItem> inv = LokiPoe.ObjectManager.Me.Inventory.Flasks.Items;
                return from item in inv
                    let flask = item.Flask
                    where flask != null && item.Name == "Granite Flask" && flask.CanUse
                    select item;
            }
        }

        private IEnumerable<InventoryItem> JadeFlasks
        {
            get
            {
                IEnumerable<InventoryItem> inv = LokiPoe.ObjectManager.Me.Inventory.Flasks.Items;
                return from item in inv
                        let flask = item.Flask
                        where flask != null && item.Name == "Jade Flask" && flask.CanUse
                        select item;
            }
        }

        private Composite CreateFlaskLogic()
        {
            return new PrioritySelector(
                new Decorator(ret => _flaskCd.IsFinished && Me.HealthPercent < 70 && LifeFlasks.Count() != 0 && !Me.HasAura("flask_effect_life"),
                    new Action(ret =>
                    {
                        LifeFlasks.First().Use();
                        _flaskCd.Reset();
                    })),
                new Decorator(ret => _flaskCd.IsFinished && Me.ManaPercent < 50 && ManaFlasks.Count() != 0 && !Me.HasAura("flask_effect_mana"),
                    new Action(ret =>
                    {
                        ManaFlasks.First().Use();
                        _flaskCd.Reset();
                    })),
                new Decorator(ret => _flaskCd.IsFinished && Me.HealthPercent < 58 && GraniteFlasks.Count() != 0 && !Me.HasAura("flask_effect_granite"),
                    new Action(ret =>
                    {
                        GraniteFlasks.First().Use();
                        _flaskCd.Reset();
                    })),
                new Decorator(ret => _flaskCd.IsFinished && Me.HealthPercent < 66 && JadeFlasks.Count() != 0 && !Me.HasAura("flask_effect_jade"),
                    new Action(ret =>
                    {
                        JadeFlasks.First().Use();
                        _flaskCd.Reset();
                    }))
                );
        }

        #endregion

        #region Register Spell

        private void Register(string spell) { Register(spell, ret => true); }

        private void Register(string spell, SpellManager.GetSelection<bool> requirement)
        {
            Register(spell, requirement, ret => Target.BestTarget);
        }

        private void Register(string spell, SpellManager.GetSelection<bool> requirement, SpellManager.GetSelection<NetworkObject> on)
        {
            // If we don't have the spell, or it's a totem, ignore it
            if (!SpellManager.HasSpell(spell, true) || SpellManager.GetSpell(spell).GetStat(StatType.IsTotem) != 0)
                return;

            Log.Debug("• Registered " + spell);
            CombatComposite.AddChild(SpellManager.CreateSpellCastComposite(spell, requirement, on));
        }

        #endregion

        #region Spell Implementation

        private void RegisterMainAbilities()
        {
            // TODO: Add vaal's new spells

            // Leap to reach best target, adjust range tho
            Register("Leap Slam", ret => Target.BestTarget.Distance > 30);

            // TODO: Check if we have a minion turned or not (and activate the spell aswell, not now since I guess it would slow runs)
            //Register("Dominating Blow", ret => NumDominatedMonsters == 0);

            Register("Infernal Blow");

            // And fall back to the default if we can...
            Register("Default Attack");
        }

        #endregion
    }

}
